from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
from django.http import HttpResponseRedirect
from posecco.forms import RFCForm, WorkOrderForm, UserForm, InfrastructureForm, ServiceForm, UploadForm, ExportForm
from posecco.models import RFC, WorkOrder, Infrastructure, Service
from django.contrib.auth import authenticate, login
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
from django.utils.translation import ugettext as _
from xml.dom import minidom
from django.contrib.auth.models import Group, User
import time
import os, sys
sys.path.append(os.pardir)
os.environ["DJANGO_SETTINGS_MODULE"] = "settings"

#write request-variables for user groups to access in templates
def get_my_roles(request):
    user = None
    if request.user.is_authenticated():
        username = request.user
        user = User.objects.get(username=username)
        request.sysadmin = Group.objects.get(name="System Administrator") in user.groups.all()
        request.change_manager = Group.objects.get(name="Change Manager") in user.groups.all()
        request.approver = Group.objects.get(name="Approver") in user.groups.all()
        request.cms_user = Group.objects.get(name="CMS User") in user.groups.all()
    
    return user

#display start/login page
def home(request):
    #load user and groups
    user = get_my_roles(request)
    
    state = "Please log in below..."
    username = password = ''
    if request.POST:
        username = request.POST.get('username')
        password = request.POST.get('password')

        user = authenticate(username=username, password=password)
        if user is not None:
            if user.is_active:
                login(request, user)
                state = "You're successfully logged in!"
                return HttpResponseRedirect('/posecco/rfclist/')
            else:
                state = "Your account is not active, please contact the site admin."
        else:
            state = "Your username and/or password were incorrect."

    request.session.flush()

    return render_to_response('login.html', {'state':state, 'username': username}, context_instance=RequestContext(request))

#display list of rfcs
def rfclist(request):
    #load user and groups
    user = get_my_roles(request)

    #get rfcs from db
    rfcs = RFC.objects.all().order_by('-updateDate')
    for rfc in rfcs:
        if rfc.lockTime is not None and time.time() - time.mktime(rfc.lockTime.timetuple()) > 1800:
            rfc.lockTime = None;
            rfc.lockUser = None;
            rfc.save()
    
    return render_to_response('rfclist.html', locals(), context_instance=RequestContext(request))

#edit single rfc
def rfc(request, rfc_id):
    #load user and groups
    user = get_my_roles(request)
    workorder = None
    
    #if id is 0 create new rfc, else get rfc from db
    if rfc_id == '0':
        rfc = RFC()
        rfc.status = 1
        rfc.initiator = request.user
    else:
        rfc = get_object_or_404(RFC, pk=rfc_id)

    #get list of workorders
    workorders = WorkOrder.objects.filter(rfc=rfc).order_by('pk')

    #post is form save
    if request.method == "POST":
        rfc_form = RFCForm(request.POST,instance=rfc)
        #if form is valid save to db
        if request.POST.__contains__("back"):
            return rfclist(request)
        else:
            if rfc_form.is_valid():
                rfc = rfc_form.save()
                if rfc.status == 3 and request.approver:
                    if request.POST.__contains__("accept"):
                        rfc.status = 4
                    elif request.POST.__contains__("reject"):
                        rfc.status = 5
                if rfc.status == 4 and workorders.count() == 0: #create workorder if status is approved
                    workorder = WorkOrder(rfc=rfc)
                    workorder.description = rfc.description
                    workorder.status = 1
                    workorder.save();
                if request.change_manager and rfc.status == 2:
                    rfc.status = 3 # set to status in progress
                if rfc.status == 1:
                    rfc.status= 2
                rfc.updateUser = user
                rfc.lockTime = None
                rfc.lockUser = None
                rfc.save()
                return rfclist(request)
    #non post --> get = display form
    else:
        #if rfc is locked for over 30 minutes, ignore it and edit anyway - most possibly it was not closed properly
        rfc_form = RFCForm(instance=rfc)
        if rfc.pk > 0:
            if (rfc.lockUser == user) or (rfc.lockTime == None) or (time.time() - time.mktime(rfc.lockTime.timetuple()) > 1800):
                rfc.lockTime = time.strftime("%Y-%m-%d %H:%M:%S")
                rfc.lockUser = user
                rfc.save()
            else:
                return rfclist(request)
    rfc_form.set_formfields(user, rfc)
    rfc_form.fields['owner'].queryset = User.objects.filter(groups__name='System Administrator')
    return render_to_response('rfc.html', locals(), context_instance=RequestContext(request))

#display list of workorders
def workorderlist(request):
    #load user and groups
    user = get_my_roles(request)
    
    #get workorders from db
    workorders = WorkOrder.objects.all().order_by('description')
    
    for workorder in workorders:
        if workorder.lockTime is not None and time.time() - time.mktime(workorder.lockTime.timetuple()) > 1800:
            workorder.lockTime = None;
            workorder.lockUser = None;
            workorder.save()
    
    return render_to_response('workorderlist.html', locals(), context_instance=RequestContext(request))

#edit single workorder
def workorder(request, workorder_id, rfc_id):
    #load user and groups
    user = get_my_roles(request)
    
    rfctmp = get_object_or_404(RFC, pk=rfc_id)

    #if id is 0 create new workorder, else get workorder from db
    if workorder_id == '0':
        workorder = WorkOrder(rfc=rfctmp)
    else:
        workorder = get_object_or_404(WorkOrder, pk=workorder_id)

    #post is form save
    if request.method == "POST":
        if request.POST.__contains__("close"):
            return rfclist(request)
        else:
            workorder_form = WorkOrderForm(request.POST,instance=workorder)
            #if form is valid save to db
            if workorder_form.is_valid():
                workorder.updateUser = user
                workorder.lockTime = None
                workorder.lockUser = None
                if workorder.status == 2 and user == workorder.responsible:
                    if request.POST.__contains__("resolve"):
                        workorder.status = 4
                    elif request.POST.__contains__("reject"):
                        workorder.status = 3
                if Group.objects.get(name="Approver") in user.groups.all() and workorder.status <= 1:
                    workorder.status = 2 # set to status in progress
                workorder = workorder_form.save()
                if workorder.status == 3: # close rfc with work order when rejected
                    workorder.rfc.status = 5
                    workorder.rfc.lockTime = None
                    workorder.rfc.lockUser = None
                    workorder.rfc.save()
                    return HttpResponseRedirect('/posecco/rfclist/')
                if workorder.status == 4: # close rfc with work order when resolved
                    workorder.rfc.status = 6
                    workorder.rfc.lockTime = None
                    workorder.rfc.lockUser = None
                    workorder.rfc.save()
                    return HttpResponseRedirect('/posecco/rfclist/')
                return HttpResponseRedirect('/posecco/rfc/%s/' % rfc_id)
    #non post --> get = diplay form
    else:
        if (workorder.lockUser == user) or (workorder.lockTime == None) or (time.time() - time.mktime(workorder.lockTime.timetuple()) > 1800):
            workorder.lockTime = time.strftime("%Y-%m-%d %H:%M:%S")
            workorder.lockUser = user
            workorder.save()
        workorder_form = WorkOrderForm(instance=workorder)

    workorder_form.set_formfields(user, workorder)
    workorder_form.fields['responsible'].queryset = User.objects.filter(groups__name='System Administrator')
    return render_to_response('workorder.html', locals(), context_instance=RequestContext(request))

#display list of workorders
def userlist(request):
    #load user and groups
    user = get_my_roles(request)
    
    #get users from db
    users = User.objects.all().order_by('username')
    
    return render_to_response('userlist.html', locals(), context_instance=RequestContext(request))

#edit single user
def user(request, user_id):
    #load user and groups
    user = get_my_roles(request)
    
    #if id is 0 create new user, else get user from db
    if user_id == '0':
        userobj = User()
    else:
        userobj = get_object_or_404(User, pk=user_id)

    #post is form save
    if request.method == "POST":
        user_form = UserForm(request.POST,instance=userobj)
        #if form is valid save to db
        if user_form.is_valid():
            userobj = user_form.save()
            return userlist(request)
    #non post --> get = diplay form
    else:
        user_form = UserForm(instance=userobj)

    user_form.set_formfields(user, userobj)
    return render_to_response('user.html', locals(), context_instance=RequestContext(request))

#display list of infrastructures
def infrastructurelist(request):
    #load user and groups
    user = get_my_roles(request)
    
    #get infrastructures from db
    infrastructures = Infrastructure.objects.all().order_by('infrastructure')
    
    return render_to_response('infrastructurelist.html', locals(), context_instance=RequestContext(request))

#edit single infrastructure
def infrastructure(request, infrastructure_id):
    #load user and groups
    user = get_my_roles(request)
    
    #if id is 0 create new infrastructure, else get infrastructure from db
    if infrastructure_id == '0':
        infrastructure = Infrastructure()
    else:
        infrastructure = get_object_or_404(Infrastructure, pk=infrastructure_id)

    #post is form save
    if request.method == "POST":
        infrastructure_form = InfrastructureForm(request.POST,instance=infrastructure)
        #if form is valid save to db
        if infrastructure_form.is_valid():
            infrastructure = infrastructure_form.save()
            return infrastructurelist(request)
    #non post --> get = diplay form
    else:
        infrastructure_form = InfrastructureForm(instance=infrastructure)

    return render_to_response('infrastructure.html', locals(), context_instance=RequestContext(request))

#display list of services
def servicelist(request):
    #load user and groups
    user = get_my_roles(request)
    
    #get services from db
    services = Service.objects.all().order_by('service')
    
    return render_to_response('servicelist.html', locals(), context_instance=RequestContext(request))

#edit single service
def service(request, service_id):
    #load user and groups
    user = get_my_roles(request)
    
    #if id is 0 create new service, else get service from db
    if service_id == '0':
        service = Service()
    else:
        service = get_object_or_404(Service, pk=service_id)

    #post is form save
    if request.method == "POST":
        service_form = ServiceForm(request.POST,instance=service)
        #if form is valid save to db
        if service_form.is_valid():
            service = service_form.save()
            return servicelist(request)
    #non post --> get = diplay form
    else:
        service_form = ServiceForm(instance=service)

    return render_to_response('service.html', locals(), context_instance=RequestContext(request))

class XMLWriter:
    """Helper class to write out an xml file"""
    def __init__(self, pretty=True):
        """Set pretty to True if you want an indented XML file"""
        self.output = u""
        self.stack = []
        self.pretty = pretty
        
    def openfile(self, tag):
        """Add an open tag"""
        self.stack.append(tag)
        if self.pretty:
            self.output += u"  "*(len(self.stack) - 1);
        self.output += u"<" + tag + u">"
        if self.pretty:
            self.output += u"\n"
        
    def close(self):
        """Close the innermost tag"""
        if self.pretty:
            self.output += u"\n" + u"  "*(len(self.stack) - 1);
        tag = self.stack.pop()
        self.output += u"</" + tag + u">"
        if self.pretty:
            self.output += u"\n"
        
    def closeAll(self):
        """Close all open tags"""
        while len(self.stack) > 0:
            self.close()
        
    def content(self, text):
        """Add some content"""
        if self.pretty:
            self.output += u"  "*len(self.stack);
        self.output += unicode(text)

    def save(self, filename):
        """Save the data to a file"""
        self.closeAll()
        fp = open(filename, "w")
        fp.write(self.output)
        fp.close()

#export form
def export(request):
    #load user and groups
    user = get_my_roles(request)
    
    #if id is 0 create new service, else get service from db
    if request.method == "POST":
        return export_file(request.POST)
    #non post --> get = diplay form
    else:
        export_form = ExportForm()

    return render_to_response('export.html', locals(), context_instance=RequestContext(request))


def export_file(request):
    writer = XMLWriter(pretty=True)
    writer.openfile("PoSecCo-Export")
    queryset = RFC.objects.all()
    for item in request:
        if item != "" and request[item] != "" and item != u'csrfmiddlewaretoken':
            name = {}
            name[item] = request[item]
            queryset = queryset.filter(**name)
    
    writer.openfile("RFCs")
    
    for item in queryset:
        writer.openfile("RFC")
        if item.infrastructure.all().count() > 0:
            writer.openfile('infrastructures')
            for field in item.infrastructure.all():
                writer.openfile('infrastructure')
                writer.content(field.infrastructure)
                writer.close()
            writer.close()
        if item.services.all().count() > 0:
            writer.openfile('services')
            for field in item.services.all():
                writer.openfile('service')
                writer.content(field.service)
                writer.close()
            writer.close()
        for field in item._meta.fields:
            writer.openfile(field.name)
            value = getattr(item, field.name)
            if value != None:
                if isinstance(value, User):
                    # This field is a foreign key, so save the primary key
                    # of the referring object
                    pk_value = getattr(value, "username")
                    writer.content(pk_value)
                else:
                    writer.content(value)
            writer.close()
        writer.close()
    writer.close()
    writer.close()
    response = HttpResponse(writer.output, content_type='text/plain')
    response['Content-Disposition'] = 'attachment; filename=export.xml'
    return response

def validate(xml):
    #TODO actually validate data
    return True

# returns an object from a content of an xml-node
# parameters are:
# item - xml node item
# fieldname - name of the field in the model
# obj - object to be edited
# model - target model
# namefield - string that defines which field of the target model shall be used if the value is not an integer - 
#             if it is an integer, the primary key will be used to find the object
# create - if set to True, a new object will be created if no object could be found by primary key or namefield
# many2many - boolean if the field is a many-to-many field
def set_value_from_xml(item, fieldname, obj, model, namefield, create, many2many):
    if many2many:
        title = fieldname
        supertitle = fieldname
        if fieldname[fieldname.__len__()-1]=="s":
            title = fieldname[:fieldname.__len__()-1]
        else:
            supertitle = fieldname + "s"
        if item.tagName == supertitle:
            for subitem in item.childNodes:
                if subitem.nodeType == subitem.ELEMENT_NODE:
                    if subitem.tagName == title:
                        if subitem.childNodes[0].nodeValue.strip().isdigit():
                            getattr(obj, fieldname).add(model.objects.get(pk=subitem.childNodes[0].nodeValue))
                        elif subitem.childNodes[0].nodeValue.strip() != '':
                            name = {}
                            name[namefield + '__iexact'] = subitem.childNodes[0].nodeValue.strip()
                            objects = model.objects.filter(**name)
                            if objects.count() > 0:
                                getattr(obj, fieldname).add(model.objects.get(**name))
                            elif create:
                                new_object = model()
                                setattr(new_object, namefield, subitem.childNodes[0].nodeValue.strip())
                                new_object.save()
                                getattr(obj, fieldname).add(new_object)
    elif item.tagName == fieldname:
        if model:
            if item.childNodes[0].nodeValue.strip().isdigit():
                setattr(obj, fieldname, model.objects.get(pk=item.childNodes[0].nodeValue.strip()))
            elif item.childNodes[0].nodeValue.strip() != "":
                name = {}
                name[namefield + '__iexact'] = item.childNodes[0].nodeValue.strip()
                objects = model.objects.filter(**name)
                if objects.count() > 0:
                    setattr(obj, fieldname, objects[0])
                elif create:
                    new_object = model()
                    setattr(new_object, namefield, item.childNodes[0].nodeValue.strip())
                    new_object.save()
                    setattr(obj, fieldname, new_object)
        elif item.childNodes[0].nodeValue.strip() != '':
            setattr(obj, fieldname, item.childNodes[0].nodeValue.strip())

def set_values_from_xml(node, obj, defaultfields, modelfields, m2mfields):
    for item in node.childNodes:
        if item.nodeType == item.ELEMENT_NODE:
            for field in defaultfields:
                set_value_from_xml(item, field, obj, None, None, None, None)
            for field in modelfields:
                set_value_from_xml(item, field[0], obj, field[1], field[2], False, None)
    obj.save()
    for item in node.childNodes:
        if item.nodeType == item.ELEMENT_NODE:
            for field in m2mfields:
                set_value_from_xml(item, field[0], obj, field[1], field[2], True, True)

def read_rfc(xml, user):
    statusMessage = ""
    if validate(xml):
        #TODO make nicer
        n = xml.childNodes[0]
        for supernode in n.childNodes:
            if supernode.nodeType == supernode.ELEMENT_NODE:
                if supernode.tagName == 'RFCs':
                    for node in supernode.childNodes:
                        try:
                            if node.nodeType == node.ELEMENT_NODE:
                                if node.tagName == 'RFC':
                                    new_rfc = RFC()
                                    new_rfc.status = 1
                                    new_rfc.initiator = user
                                    set_values_from_xml(node, new_rfc, 
                                                        ('summary', 
                                                         'description', 
                                                         'status', 
                                                         'priority'), 
                                                        (('owner', User, 'username'), 
                                                         ('initiator', User, 'username')), 
                                                        (('infrastructure', Infrastructure, 'infrastructure'), 
                                                         ('services', Service, 'service')))
                                    statusMessage += "RFC " + new_rfc.summary + " added, "
                        except Exception, x:
                            statusMessage += '%s, ' % x
        return _(statusMessage)
    else:
        return _('Invalid data entered')

def import_rfc(request):
    return HttpResponse('OK')

@login_required
def upload(request):
    #load user and groups
    user = get_my_roles(request)
    
    if request.method == 'POST':
        uform = UploadForm(request.POST, request.FILES)
        if uform.is_valid():
            ufile = request.FILES['file']
            try:
                xml = minidom.parse(ufile)
                response = read_rfc(xml, request.user)
            except Exception, x:
                response = _('Please upload valid data %s' % x)
    else:
        uform = UploadForm()
    return render_to_response('upload.html', locals(), context_instance=RequestContext(request))
